///////////////////////////////////////////////////////////////////////////////
//
// This example demonstrates how to set an avi sink type and to set the parameter
// for a video codec (compressor). It shows, how to retrieve the data in a memory
// block and save it into a binary file.
//
#define _WIN32_WINNT 0x0601

#include <iostream>
#include <tisudshl.h>

#include "../Common/CmdHelper.h"

using namespace _DSHOWLIB_NAMESPACE;

struct codec_entry
{
	codec_entry( std::string d, tCodecPtr p, GUID u )
		: desc( d ), codec( p ), uncompressed_subtype( u ) {}

    std::string	desc;
    tCodecPtr	codec;
    GUID		uncompressed_subtype;
};

static std::ostream&	operator<<( std::ostream& str, const codec_entry& item )
{
	return str << item.desc;
}

static tMediaStreamSinkPtr	selectCodecAndCreateSink( tMediaStreamContainerPtr& pCont )
{
//<<selectcodec
	// This will contain available codecs/uncompressed formats for this container
	std::vector<codec_entry> codecList;

	tCodecList avCodecList = Codec::getAvailableCodecList();
	for( tCodecList::iterator it = avCodecList.begin(); it != avCodecList.end(); ++it )
	{
		tCodecPtr pcodec = *it;
		// Only add Codecs the container supports
		if( pCont->isCustomCodecSupported( pcodec ) )
		{
			codecList.push_back( codec_entry( pcodec->getName(), pcodec, GUID_NULL ) );
		}
	}

	// Only add uncompressed types when the container supports that
	if( pCont->isUncompressedSupported() )
	{
        codecList.push_back( codec_entry( "Uncompressed RGB32", nullptr, MEDIASUBTYPE_RGB32 ) );
        codecList.push_back( codec_entry( "Uncompressed RGB24", nullptr, MEDIASUBTYPE_RGB24 ) );
        codecList.push_back( codec_entry( "Uncompressed Y800", nullptr, MEDIASUBTYPE_Y800 ) );
        codecList.push_back( codec_entry( "Uncompressed UYVY", nullptr, MEDIASUBTYPE_UYVY ) );
	}

	if( codecList.empty() )
	{
		// there are no entries in the codec list, so just create a sink only containing the container
        return MediaStreamSink::create( pCont );
	}

    codecList.insert( codecList.begin(), codec_entry( "No codec", nullptr, GUID_NULL ) );

    int idx = presentUserChoice( codecList );
    if( idx == -1 )
    {
        std::cerr << "No choice was made from the codec list." << std::endl;
        return nullptr;
    }

	codec_entry choice = codecList.at(idx);
	if( choice.codec == NULL )
	{
		// The user chose a uncompressed type
        return MediaStreamSink::create( pCont, choice.uncompressed_subtype );
	}

	// The user chose a codec. 
	// Open the dialog if available and then create the according sink using codec and container
	if( choice.codec->hasDialog() )
	{
		std::cout << "Setup the codec: " << std::endl;
		choice.codec->callDialog();
	}
	// Create the sink with the specified codec.
	return MediaStreamSink::create( pCont, choice.codec );
	
//>>selectcodec
}

int main()
{
	// Initialize the library
    InitLibrary();

//<<setupdev
	Grabber grabber;
	if( !setupDeviceFromFile( grabber ) )
	{
		return 1;
	}
//>>

	// disable overlay
	grabber.setOverlayBitmapPathPosition( ePP_NONE );

//<<seleccontainer
	/* Choose the MediaStreamContainer which presents the container format for
	 * the video file you want to write. This can be, for example, "Avi" or "Ogm".
	 */
	tMediaStreamContainerList container_list = MediaStreamContainer::getAvailableMediaStreamContainerList();
	if( container_list.empty() )
	{
		std::cerr << "No MediaStreamContainer found." << std::endl;
		return 1;
	}

	int FWSC_idx = presentUserChoice( toStringArrayPtrList( container_list ) );
	if( FWSC_idx == -1 )
	{
		std::cerr << "No MediaStreamContainer chosen." << std::endl;
		return 1;
	}

	tMediaStreamContainerPtr pCont = container_list.at( FWSC_idx );
//>>

	/* Some containers do not allow you to specify a codec for compressing
	 * the video data that goes to the file. E.g. MPEG2 Encoders do not support
	 * any other codec then MPEG2, so no choice is presented for these Containers.
	 */
    smart_ptr<MediaStreamSink> pSink = selectCodecAndCreateSink( pCont );
    if( pSink == 0 )
    {
        std::cerr << "The selected sink could not be built." << std::endl;
        return 1;
    }

	std::string filename;
	std::cout << "\nVideo filename: " << std::flush;
	std::cin >> filename;
	if( filename.empty() )
	{
		std::cerr << "Bad filename specified." << std::endl;
		return 1;
	}

	pSink->setFilename( filename );

//<<usesink
	// The sink is initially paused, so that no video data is written to the file.
	pSink->setSinkMode( GrabberSinkType::ePAUSE );

	// apply the sink
	grabber.setSinkType( pSink );
	
	// Start the live mode. The live video will be displayed but no images will be written
	// to the AVI file because pSink is in pause mode.
	if( !grabber.startLive( true ) )
	{
		std::cerr << grabber.getLastError().toString() << std::endl;
		return -1;
	}
	
	fflush(stdin);

	std::cout << "Press [enter] to start capturing!";
	std::cin.get();

	// Start the sink. The image stream is written to the AVI file.
	pSink->setSinkMode(GrabberSinkType::eRUN );

	std::cout << "Video recording started." << std::endl;
	std::cout << "Press [enter] to stop capturing!";
	std::cin.get();

	// Stop the live mode. This stops writing the avi file to disk.
	grabber.stopLive();
//>>
	return 0;
}
